﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Net;
using System.Net.Sockets;

namespace Framework.Network
{
    public class Server
    {
        static int port;
        static IPAddress ip;
        static Socket sock;
        static byte[] buffer;

        static Server()
        {
            port = 15778;
            ip = IPAddress.Any;
            sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            buffer = new byte[8192];
        }

        public static void Start()
        {
            if (!PortIsAvailable(port))
            {
                Console.WriteLine("Cannot start server on port {0}. Port is unavailable. ", port);
                return;
            }

            try
            {
                IPEndPoint endpoint = new IPEndPoint(ip, port);

                //Bind endpoint
                sock.Bind(endpoint);
                //Listen
                sock.Listen(5);

                //Accept connections
                sock.BeginAccept(new AsyncCallback(OnAccept), sock);

                Console.ForegroundColor = ConsoleColor.Green;
                Console.WriteLine("Server listening on: {0}:{1}", ip, port);
                Console.ResetColor();
            }
            catch (Exception e)
            {
                if (e is SocketException)
                {
                    SocketException se = (SocketException)e;

                    Console.WriteLine("-- Socket Error ({0}) --", se.ErrorCode);
                    Console.WriteLine("Source: Server.Start()");
                    Console.WriteLine("Error: {0}", se.Message);
                }
            }
        }

        private static void OnAccept(IAsyncResult ar)
        {
            Socket s = (Socket)ar.AsyncState;

            try
            {
                Socket c = s.EndAccept(ar);
                Console.WriteLine("Connection accepted from {0}", c.RemoteEndPoint);

                try
                {
                    //Receive data
                    c.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(OnReceive), c);
                }
                catch (Exception e)
                {
                    if (e is SocketException)
                    {
                        SocketException se = (SocketException)e;

                        Console.WriteLine("-- Socket Error ({0}) --", se.ErrorCode);
                        Console.WriteLine("Source: Server.OnAccept(IAsyncResult)");
                        Console.WriteLine("Error: {0}", se.Message);
                    }
                }
                //Accept more clients
                s.BeginAccept(new AsyncCallback(OnAccept), s);
            }
            catch (Exception e)
            {
                if (e is SocketException)
                {
                    SocketException se = (SocketException)e;

                    Console.WriteLine("-- Socket Error ({0}) --", se.ErrorCode);
                    Console.WriteLine("Source: Server.OnAccept(IAsyncResult)");
                    Console.WriteLine("Error: {0}", se.Message);
                }
            }
        }

        private static void OnReceive(IAsyncResult ar)
        {
            Socket c = (Socket)ar.AsyncState;
            try
            {
                int rv = c.EndReceive(ar);
                if (rv > 0)
                {
                    byte[] data = new byte[rv];
                    //Copy buffer to new array
                    Array.Copy(buffer, 0, data, 0, data.Length);


                }

                Array.Clear(buffer, 0, buffer.Length);
                c.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(OnReceive), c);
            }
            catch (Exception e)
            {
                SocketException se = (SocketException)e;

                Console.WriteLine("-- Socket Error ({0}) --", se.ErrorCode);
                Console.WriteLine("Source: Server.OnAccept(IAsyncResult)");
                Console.WriteLine("Error: {0}", se.Message);
            }
        }


        private static bool PortIsAvailable(int port)
        {
            IPEndPoint endpoint = new IPEndPoint(IPAddress.Any, port);
            Socket s = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            try
            {
                s.Bind(endpoint);
                s.Listen(5);
                s.Close();
            }
            catch (SocketException se)
            {
                if (se.ErrorCode == 10048)
                {
                    return false;
                }
            }

            return true;
        }
    }
}
